/*
 * File: cmd.c
 * ChangeLog
 * Date             Author          Note
 * 2014.10.16       Herbert         First Version
 */
#include <stdlib.h>
#include <string.h>
#include "cmd.h"
#include "udp.h"
#include <assert.h>
#include <stdio.h>

uchar send_buf[SEND_BUF_SIZE];
uchar recv_buf[RECV_BUF_SIZE];

/* 数据校验和生成 */
unsigned short checksum(unsigned short *data, unsigned short size)
{
    long sum = 0;
    unsigned short d = 0;
    uchar *p = (uchar*)data;
    int i = 0;

    while (size > 1)
    {
        d = p[i] | (p[i + 1] << 8);
        sum += d;
        i += 2;
        size -= sizeof(unsigned short);
    }

    if (size > 0)
    {
        sum += p[i];
    }

    while (sum >> 16)
        sum = (sum >> 16) + (sum & 0xFFFF);

    sum = ~sum & 0xFFFF;
    if (!sum)
        sum = 0xFFFF;

    return (unsigned short)sum;
}
/*
 * @ret: 校验成功返回0， 失败返回-1
 * NOTE: 校验接收到的数据
 */
int cmd_check_data()
{
    ushort size = recv_buf[2] | (recv_buf[3] << 8);
    ushort cs = recv_buf[4 + size] | (recv_buf[5 + size] << 8);
    if ((recv_buf[0] != CMD_SIGNATURE0) ||
        (recv_buf[1] != CMD_SIGNATURE1) ||
        (recv_buf[6 + size] != CMD_EOF))
    {
        printf("signature or eof error\n");
        return -1;
    }

    if (checksum((ushort*)&recv_buf[4], size) != cs)
    {
        printf("checksum error\n");
        return -1;
    }
    return 0;
}

/*
 * @param data 要发送的数据
 * NOTE: 填充发送缓冲区，返回发送的总大小
 */
int cmd_setup_send_data(uchar *data, ushort size)
{
    assert(data != NULL);
    int i = 0;
    ushort cs = checksum((ushort*)data, size);
    //printf("cs = %x\n", cs);
    send_buf[0] = CMD_SIGNATURE & 0x00FF;
    send_buf[1] = (CMD_SIGNATURE & 0xFF00) >> 8;
    send_buf[2] = size & 0x00FF;
    send_buf[3] = (size & 0xFF00) >> 8;

    for (i = 0; i < size; i++)
    {
        send_buf[4 + i] = data[i];
    }
    send_buf[4 + size] = cs & 0x00FF;
    send_buf[5 + size] = (cs & 0xFF00) >> 8;
    send_buf[6 + size] = CMD_EOF;

    return 7 + size;
}


/*
 * @param[out]:data-协议中的data部分
 * @ret:数据格式错误返回-1，成功返回0
 * NOTE:将接收到的数据解析成cmd格式
 */
int cmd_parse_recv_data(uchar *data, int size)
{
    assert(data != NULL);
    int i = 0;
    int recv_size = 0;
    if (cmd_check_data() < 0)
    {
        return -1;
    }

    recv_size = recv_buf[2] | (recv_buf[3] << 8);
    if (recv_size != size)
    {
//        printf("revc_size = %d, size = %d\n", recv_size, size);
        return -1;
    }

    for (i = 0; i < size; i++)
    {
        data[i] = recv_buf[i + 4];
    }

    return 0;
}
/*
 * @param query      查询命令
 * @param query_size 查询命令大小
 * @param resp       响应内容
 * @param resp_size  响应内容大小
 * @ret: 通讯失败返回-1，正常返回0
 */
int cmd_query(MH_UDP *pudp, uchar *query, int query_size, RESPONSE *resp, int resp_size)
{
    assert(query != NULL);
    assert(resp != NULL);
    int send_size;
    int retry = 1;

    ushort cmd = query[0] | (query[1] << 8);
    send_size = cmd_setup_send_data(query, query_size);

    if (udp_send_data(pudp, (char*)send_buf, send_size) < 0)
    {
        return -1;
    }
RETRY:
    if (udp_recv_data(pudp, (char*)recv_buf, RECV_BUF_SIZE) < 0)
    {
       // printf("recv data err\n");
        return -1;
    }
    if (cmd_parse_recv_data((uchar*)resp, resp_size) < 0)
    {
        if (retry-- > 0)
        {
            goto RETRY;
        }
        printf("parse recv data failed\n");
        return -1;
    }
    if (resp->type != cmd)
    {
        if (retry-- > 0)
        {
            goto RETRY;
        }
        printf(" recv data of type err\n");
        return -1;
    }
    return resp->state;
}

/*
 * @param[in]  pmodify 修改命令
 * @param[out] data 转换成要发出去的数据
 * NOTE: 将modify命令转换成数组
 */
void cmd_setup_modify_data(MODIFY* pmodify, uchar *pdata)
{
    assert(pmodify != NULL);
    assert(pdata != NULL);
    int offset = (int)&((MODIFY*)0)->content;
    memcpy((void*)pdata, (void*)pmodify, offset);
    memcpy((void*)(pdata + offset), (void*)pmodify->content, pmodify->size);
}

/*
 * @param pmodify 修改命令
 */
int cmd_modify(MH_UDP *pudp, MODIFY *pmodify)
{
    assert(pmodify != NULL);
    RESPONSE resp = { 0 };
    int send_size = 0;
    int ret = -1;
    int retry = 1;
    int size = (int)&((MODIFY*)0)->content + pmodify->size;
    uchar *pdata = (uchar*)calloc(size, sizeof (uchar));
    cmd_setup_modify_data(pmodify, pdata);
    send_size = cmd_setup_send_data(pdata, size);
    if (udp_send_data(pudp, (char*)send_buf, send_size) < 0)
    {
        goto MODIFY_FAIL;
    }
RETRY:
    if (udp_recv_data(pudp, (char*)recv_buf, RECV_BUF_SIZE) < 0)
    {
        goto MODIFY_FAIL;
    }

    if (cmd_parse_recv_data((uchar*)&resp, sizeof (resp)) < 0)
    {
        if (retry-- > 0)
        {
            goto RETRY;
        }
        goto MODIFY_FAIL;
    }
    if (resp.type != pmodify->cmd)
    {
        goto MODIFY_FAIL;
    }
    ret = resp.state;
MODIFY_FAIL:
    free(pdata);
    return ret;
}


/*
 * @param pmodify 修改命令
 */
int cmd_request(MH_UDP *pudp, uchar *request, int size)
{
    assert(request != NULL);
    RESPONSE resp = { 0 };
    int send_size = 0;
    int retry = 1;
    //int i = 0;
    ushort cmd = request[0] | (request[1] << 8);
    send_size = cmd_setup_send_data(request, size);

    if (udp_send_data(pudp, (char*)send_buf, send_size) < 0)
    {
        return -1;
    }
RETRY:
    if (udp_recv_data(pudp, (char*)recv_buf, RECV_BUF_SIZE) < 0)
    {
//        printf("udp recv data error\n");
        return -1;
    }

    if (cmd_parse_recv_data((uchar*)&resp, sizeof (resp)) < 0)
    {
        if (retry-- > 0)
        {
            goto RETRY;
        }
        return -1;
    }

    if (resp.type != cmd)
    {
        return -1;
    }
    return resp.state;
}

/*
 * @param pmodify 修改命令
 */
int cmd_request_just(MH_UDP *pudp, uchar *request, int size)
{
    int ret = 0;
    assert(request != NULL);
    int send_size = 0;

    send_size = cmd_setup_send_data(request, size);

    if (udp_send_data(pudp, (char*)send_buf, send_size) < 0)
    {
        return -1;
    }
    return ret;
}




























